# @author: Caijiaming
# @Time 2023/9/4 16:50

#玛丽冒险游戏

#引入pygame模块
import pygame
#导入pygame的常量
from pygame.locals import *
#导入系统模块
import sys
#导入迭代工具
from itertools import cycle

SCREENWIDTH = 822   #窗口宽度
SCREEHEIGHT = 199   #窗口高度
FPS = 30            #更新画面时间

#定义移动地图类
class MyMap():
    def __init__(self,x,y):
        #加载背景图片
        self.bg = pygame.image.load("image/bg.png").convert_alpha()
        self.x = x
        self.y = y

    #地图滚动
    def map_rolling(self):
        #判断地图是已经移动完毕，小于790说明已经移动完毕
        if self.x < -790:
            #给地图绘制一个新的坐标点
            self.x = 800
        else:
            #向左移动5个像素
            self.x -= 5

    #实现地图的无限滚动
    def map_update(self):
        SCREEN.blit(self.bg,(self.x,self.y))

#玛丽类
class Marie():
    #初始化玛丽类参数
    def __init__(self):
        #初始化玛丽类矩形
        self.rect = pygame.Rect(0,0,0,0)
        #跳跃开关状态
        self.jumpState =  False
        #跳跃高度
        self.jumpHeight = 130
        #最低坐标
        self.lowest_y = 140
        #跳跃增变量
        self.jumpValue = 0
        #玛丽动图索引
        self.marieIndex = 0
        self.marieIndexGen = cycle([0,1,2])
        #加载玛丽人物图片,元祖
        self.adventure_img = (
            pygame.image.load("image/adventure1.png").convert_alpha(),
            pygame.image.load("image/adventure2.png").convert_alpha(),
            pygame.image.load("image/adventure3.png").convert_alpha()
        )
        #加载跳跃音效
        self.jump_audio = pygame.mixer.Sound("audio/jump.wav")
        self.rect.size = self.adventure_img[0].get_size()
        #绘制玛丽的x坐标
        self.x = 50
        #绘制玛丽的y坐标
        self.y = self.lowest_y
        self.rect.topleft = (self.x,self.y)

    #跳跃
    def jump(self):
        self.jumpState = True

    #移动
    def move(self):
        #当起跳时
        if self.jumpState:
            #如果站在地上
            if self.rect.y >= self.lowest_y:
                #以5个像素向上移动
                self.jumpValue = -5
            #玛丽跳跃到达顶部
            if self.rect.y <= self.lowest_y - self.jumpHeight:
                #以5个像素回落
                self.jumpValue = 5
            #通过循环改变玛丽的y坐标
            self.rect.y += self.jumpValue

            #如果玛丽回到地面
            if self.rect.y >= self.lowest_y:
                #关闭跳跃状态
                self.jumpState = False

    #绘制玛丽
    def draw_marie(self):
        #匹配玛丽动图
        marieIndex = next(self.marieIndexGen)
        #绘制玛丽
        SCREEN.blit(self.adventure_img[marieIndex],(self.x,self.rect.y))

#引入随机库
import random
#设置障碍物类
class Obstacle():
    #分数
    score = 1
    #移动距离
    move = 5
    #障碍物的坐标
    obstacle_y = 150

    #障碍物类初始化工作
    def __init__(self):
        # 初始化障碍物矩形
        self.rect = pygame.Rect(0,0,0,0)
        #设置障碍物图片
        self.missile = pygame.image.load("image/missile.png").convert_alpha()
        self.pipe = pygame.image.load("image/pipe.png").convert_alpha()
        #设置分数图片
        self.numbers = (
            pygame.image.load("image/0.png").convert_alpha(),
            pygame.image.load("image/1.png").convert_alpha(),
            pygame.image.load("image/2.png").convert_alpha(),
            pygame.image.load("image/3.png").convert_alpha(),
            pygame.image.load("image/4.png").convert_alpha(),
            pygame.image.load("image/5.png").convert_alpha(),
            pygame.image.load("image/6.png").convert_alpha(),
            pygame.image.load("image/7.png").convert_alpha(),
            pygame.image.load("image/8.png").convert_alpha(),
            pygame.image.load("image/9.png").convert_alpha()
        )
        #加载得分音效
        self.score_audio = pygame.mixer.Sound("audio/score.wav")
        #随机生成障碍物,0是导弹障碍物，1是管道障碍物
        r = random.randint(0,1)
        if r == 0:
            self.image = self.missile
            #导弹移动速度
            self.move = 15
            #导弹显示的位置,导弹是在天上飞
            self.obstacle_y = 100
        else:
            self.image = self.pipe

        #根据障碍物图的宽，高来设置矩形,返回的是一个元组
        self.rect.size = self.image.get_size()
        #获取图的宽，高
        self.width,self.height = self.rect.size
        #障碍物出现坐标
        self.x = 800
        self.y = self.obstacle_y
        self.rect.center = (self.x,self.y)

    #移动障碍物
    def obstacle_move(self):
        self.rect.x -= self.move

    #绘制障碍物
    def obstacle_draw(self):
        SCREEN.blit(self.image,(self.rect.x,self.rect.y))

    #获取分数
    def getScore(self):
        self.score
        tmp = self.score;
        if tmp == 1:
            #播放得分音效
            self.score_audio.play()
        self.score = 0
        return tmp

    #显示分数
    def showScore(self,score):
        #获得分数字,列表解析
        self.scoreDigits = [int(x) for x in list(str(score))]
        #要显示的所有数字的总宽度
        totalWidth = 0
        for digit in self.scoreDigits:
            totalWidth += self.numbers[digit].get_width()
        #分数显示位置
        Xoffset = (SCREENWIDTH - (totalWidth + 30))
        for digit in self.scoreDigits:
            #绘制分数
            SCREEN.blit(self.numbers[digit],(Xoffset,SCREEHEIGHT * 0.1))
            #随着数字增加改变位置
            Xoffset += self.numbers[digit].get_width()

#背景音乐播放类
class Music_Button():
    is_open = True
    #初始化
    def __init__(self):
        #打开按钮图片
        self.open_img = pygame.image.load("image/btn_open.png").convert_alpha()
        self.close_img = pygame.image.load("image/btn_close.png").convert_alpha()
        #加载播放背景音乐
        self.bg_music = pygame.mixer.Sound("audio/bg_music.wav")

    #判断鼠标是否在按钮图标范围内
    def is_select(self):
        #获取鼠标的坐标
        point_x,point_y = pygame.mouse.get_pos()
        #获取按钮图片的大小
        w,h = self.open_img.get_size()
        #判断鼠标是否在图片范围内
        in_x = point_x > 20 and point_x < 20 + w
        in_y = point_y > 20 and point_y < 20 + h
        return in_x and in_y

#游戏结束
def game_over():
    #加载人物与障碍物撞击音效
    bump_audio = pygame.mixer.Sound("audio/bump.wav")
    #撞击音效播放
    bump_audio.play()
    #获取窗体的宽高
    screen_width = pygame.display.Info().current_w
    screen_height = pygame.display.Info().current_h
    #加载游戏结束图片
    over_img = pygame.image.load("image/gameover.png").convert_alpha()
    #将游戏结束图片绘制在窗体中间位置
    SCREEN.blit(over_img,((screen_width - over_img.get_width()) / 2 ,(screen_height - over_img.get_height()) / 2))


#定义主函数
def mainGame():
    #积分
    score = 1
    #游戏结束标志
    over = False
    global SCREEN,FPSCLOCK
    # 初始化pygame
    pygame.init()
    #使用pygame,创建时钟实例
    FPSCLOCK = pygame.time.Clock()
    #创建窗体
    SCREEN = pygame.display.set_mode((SCREENWIDTH,SCREEHEIGHT))
    #设置窗体标题
    pygame.display.set_caption("玛丽冒险")
    #设置地图对象
    bg1 = MyMap(0,0)
    bg2 = MyMap(800,0)
    #创建玛丽对象
    marie = Marie()

    #添加障碍物的时间
    addObstacleTimer = 0
    #障碍物对象列表
    list = []

    #实力化背景音乐对象并播放背景音乐
    music_button = Music_Button()
    btn_img = music_button.open_img
    #循环播放背景音乐
    music_button.bg_music.play(-1)


    while True:
        #获取点击事件
        for event in pygame.event.get():
            #如果点击了关闭窗口就将窗口关闭
            if event.type == QUIT:
                #退出窗口
                pygame.quit()
                #关闭窗口
                sys.exit()

            #如果下空格键,开启跳跃状态
            if event.type == KEYDOWN and event.key == K_SPACE:
                #如果玛丽在地上
                if marie.rect.y >= marie.lowest_y:
                    #播放跳跃音效
                    marie.jump_audio.play()
                    #开启跳跃状态
                    marie.jump()

                if over == True:
                    mainGame()


            #判断背景音乐开关按钮鼠标事件
            if event.type == MOUSEBUTTONUP:
                if music_button.is_open:
                    #开关打开状态显示，关闭按钮
                    btn_img = music_button.close_img
                    #关闭北约音乐状态
                    music_button.is_open = False
                    #关闭背景音乐
                    music_button.bg_music.stop()
                else:
                    #背景音乐关闭，显示打开按钮
                    btn_img = music_button.open_img
                    music_button.is_open = True
                    music_button.bg_music.play(-1)


        if over == False:
            bg1.map_update()
            # 地图移动
            bg1.map_rolling()
            bg2.map_update()
            bg2.map_rolling()
            #跳跃
            marie.move()
            marie.draw_marie()

            #绘制添加障碍物出现的间隔时间
            if addObstacleTimer > 1300:
                r = random.randint(0,100)
                if r > 40:
                    #创建时间障碍物
                    obstacle = Obstacle()
                    #将障碍物对象添加到列表中
                    list.append(obstacle)
                #重置添加障碍物时间
                addObstacleTimer = 0

            #循环遍历障碍物
            for i in range(len(list)):
                #障碍物移动
                list[i].obstacle_move()
                #绘制障碍物
                list[i].obstacle_draw()

                #判断是否碰撞
                if pygame.sprite.collide_rect(marie,list[i]):
                    over = True
                    game_over()
                    music_button.bg_music.stop()
                else:
                    #判断玛丽是否越来障碍物
                    if(list[i].rect.x + list[i].rect.width) < marie.rect.x:
                        #越过障碍物，加分
                        #print(list[i].getScore())
                        score += list[i].getScore()
                list[i].showScore(score)


        #增加障碍时间
        addObstacleTimer += 20

        #将背景音乐开关按钮显示到主窗体中
        SCREEN.blit(btn_img,(20,20))

        #更新整个窗体
        pygame.display.update()
        #循环多长时间运行一次
        FPSCLOCK.tick(FPS)


if __name__ == '__main__':
    #调用主函数
    mainGame()

